﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using GameViewModel.Annotations;

namespace GameViewModel
{
    public class TheGame : INotifyPropertyChanged
    {
        private int _bonusScore;

        public GameOptions Options { get; set; }
        public LinkedList<Player> Players { get; set; }

        public bool IsStarted { get; set; }

        public int BonusScore
        {
            get { return _bonusScore; }
            set
            {
                if (value == _bonusScore) return;
                _bonusScore = value;
                OnPropertyChanged();
            }
        }

        public TheGame()
        {
            Options = new GameOptions();
            Players = new LinkedList<Player>();
        }

        public void SaveOptions(Action<Exception> onError)
        {
            XmlSerializationHelper.Serialize(GameOptions.FileName, Options, onError);
        }

        public void LoadOptions(Action<Exception> onError)
        {
            Options = XmlSerializationHelper.Deserialize<GameOptions>(GameOptions.FileName, onError);
        }

        public void Reset()
        {
            IsStarted = false;
        }

        public void Start()
        {
            IsStarted = true;
            BonusScore = 0;
            var selectedPersons = Options.Persons.Where(p => p.IsSelected);
            var ordered = Options.IsRandomOrder ? selectedPersons.Shuffle() : selectedPersons;
            var players = ordered.Select((p, order) => new Player(p.Name, order + 1)).ToList();
            if (players.Count() < 2)
                throw new InvalidDataException("Cannot start game with less than 2 players.");
            Players = new LinkedList<Player>(players);
            OnPropertyChanged("Players");
        }

        public void StartReversed()
        {
            IsStarted = true;
            BonusScore = 0;
            var selectedPersons = Players.Where(p => p.IsSelected);
            var ordered = selectedPersons.Reverse();
            var players = ordered.Select((p, order) => new Player(p.Name, order + 1)).ToList();
            Players.Clear();
            Players = new LinkedList<Player>(players);
            OnPropertyChanged("Players");
        }

        public void Plus(Player player)
        {
            player.Plus(Options.Unit + BonusScore);
            if (BonusScore > 0)
                BonusScore = 0;
            Players.Find(player).PreviousOrLast().Value.Minus(Options.Unit);
        }

        public void Minus(Player player)
        {
            player.Minus(Options.Unit);
            Players.Find(player).PreviousOrLast().Value.Plus(Options.Unit);
        }

        public void Penalty(Player player)
        {
            player.Minus(Options.Unit);
            BonusScore += Options.Unit;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

}
